﻿using System;
using System.Threading;

namespace BizTalkFactory.Adapters.AzureStorage
{
    class StorageEventArgs : EventArgs
    {
        protected StorageEventArgs(string storageName)
        {
            StorageName = storageName;
        }

        /// <summary>
        /// Specifies whether to cancel retrieval or deletion from the underlying storage object.
        /// For instance, Azure Queue items are retrieved in a two-phase sequence. If an error occurs
        /// while retrieving a message by PeekMessage or GetMessage, set e.Cancel to prevent the
        /// message from being deleted from the Queue.
        /// </summary>
        public Boolean Cancel = false;

        /// <summary>
        /// Underlying Blob, Queue or Table storage item.
        /// </summary>
        public String StorageName;
    }

    class StorageListener
    {
        public const int MinimumPollingIntervalInSeconds = 5;

        #region Private Members

        object mutex_ = new Object();

        private int pollingIntervalInSeconds_ = 30;
        private Thread thread_ = null;
        private ManualResetEvent stopEvent_ = new ManualResetEvent(false);

        AzureStorageAdapterConnection connection_;

        #endregion

        #region Events

        public delegate void StorageEventReceivedEventHandler(object sender, StorageEventArgs e);

        public event StorageEventReceivedEventHandler Received;

        #endregion

        #region Construction

        public StorageListener(AzureStorageAdapterConnection connection)
        {
            connection_ = connection;
        }

        #endregion

        #region Attributes

        protected AzureStorageAdapterConnection Connection
        {
            get
            {
                return connection_;
            }
            set
            {
                connection_ = value;
            }
        }

        public Int32 PollingIntervalInSeconds
        {
            get
            {
                lock (mutex_) return pollingIntervalInSeconds_;
            }
            set
            {
                lock (mutex_)
                {
                    pollingIntervalInSeconds_ = value;
                    if (pollingIntervalInSeconds_ < MinimumPollingIntervalInSeconds)
                        pollingIntervalInSeconds_ = MinimumPollingIntervalInSeconds;
                }
            }
        }

        public virtual bool EnableRaisingEvents
        {
            get
            {
                return ((thread_ != null) && (thread_.IsAlive));
            }

            set
            {
                if (value)
                    Start();
                else
                    Stop();
            }
        }

        #endregion

        #region Overrides

        protected virtual void StartingListener()
        {
            /* empty stub */
        }

        protected virtual void Listening()
        {
            /* empty stub */
        }

        protected virtual void StoppingListener()
        {
            /* empty stub */
        }

        #endregion

        #region Operations

        protected void RaiseReceivedEvent(object sender, StorageEventArgs e)
        {
            if (Received != null)
                Received(this, e);
        }

        #endregion

        #region Implementation

        /// <summary>
        /// Start the internal listening thread.
        /// </summary>
        private void Start()
        {
            System.Diagnostics.Debug.Assert(thread_ == null);

            thread_ = new Thread(new ThreadStart(Listen));
            thread_.Start();
        }

        /// <summary>
        /// Main internal listening thread procedure.
        /// </summary>
        private void Listen()
        {
            StartingListener();

            try
            {
                // start listening immediately

                DateTime TimeToPoll = DateTime.MinValue;

                while (true)
                {
                    // check whether instructed to stop

                    if (stopEvent_.WaitOne(0))
                        break;

                    // check is polling interval has elapsed

                    if (DateTime.UtcNow > TimeToPoll)
                    {
                        Listening();
                        TimeToPoll = DateTime.UtcNow.Add(TimeSpan.FromSeconds(PollingIntervalInSeconds));
                        continue;
                    }

                    // yield for next loop

                    Thread.Sleep(300);
                }
            }

            catch (Exception /* e */)
            {
                // TODO: handle errors
                // TODO: create message fault
            }

            finally
            {
                StoppingListener();
            }
        }

        /// <summary>
        /// Stop the internal listening thread.
        /// </summary>
        private void Stop()
        {
            System.Diagnostics.Debug.Assert(thread_ != null);
            System.Diagnostics.Debug.Assert(thread_.IsAlive);

            stopEvent_.Set();
            thread_.Join();
        }

        #endregion
    }
}
